
/**		
 * 
 *	uk.co.lmfm.guppy.model.WorkspaceProxy
 *	
 *	@version 1.00 | Jan 15, 2010
 *	@author Justin Windle
 *  
 **/
 
package uk.co.lmfm.guppy.model 
{
	import uk.co.lmfm.guppy.application.enum.ApplicationConstants;
	import uk.co.lmfm.guppy.model.vo.CloneFileVO;
	import uk.co.lmfm.guppy.model.vo.ConfigVO;
	import uk.co.lmfm.guppy.model.vo.CoreLibraryVO;
	import uk.co.lmfm.guppy.model.vo.FlexSDKVO;
	import uk.co.lmfm.guppy.model.vo.SettingsVO;
	import uk.co.lmfm.guppy.model.vo.WorkspaceVO;
	import uk.co.lmfm.util.FileUtil;
	import uk.co.lmfm.util.StringUtil;

	import org.osflash.thunderbolt.Logger;
	import org.puremvc.as3.multicore.interfaces.IProxy;
	import org.puremvc.as3.multicore.patterns.proxy.Proxy;

	import flash.events.Event;
	import flash.events.FileListEvent;
	import flash.events.IOErrorEvent;
	import flash.filesystem.File;

	/**
	 * WorkspaceProxy
	 */
	public class WorkspaceProxy extends Proxy implements IProxy 
	{
		//	----------------------------------------------------------------
		//	CONSTANTS
		//	----------------------------------------------------------------

		public static const NAME : String = "WorkspaceProxy";
		public static const CONFIG_PATH : String = "config.xml";		public static const METADATA_PATH : String = ".metadata";

		public static const FDT_UI_PREFS_PATH : String = ".plugins/org.eclipse.core.runtime/.settings/com.powerflasher.fdt.ui.prefs";
		public static const FDT_CORE_PREFS_PATH : String = ".plugins/org.eclipse.core.runtime/.settings/com.powerflasher.fdt.core.prefs";

		public static const FDT_UI_PREFS_TEMPLATE_PATH : String = "FDT/templates/settings/com.powerflasher.fdt.ui.prefs";
		public static const FDT_CORE_PREFS_TEMPLATE_PATH : String = "FDT/templates/settings/com.powerflasher.fdt.core.prefs";		public static const FDT_WORKSPACE_TEMPLATE_PATH : String = "FDT/templates/workspace/.metadata";
		public static const FDT_SNIPPETS_TEMPLATE_PATH : String = "FDT/templates/code/templates.xml";		public static const FDT_HEADERS_TEMPLATE_PATH : String = "FDT/templates/code/codetemplate.xml";

		public static const ANT_PREFS_PATH : String = ".plugins/org.eclipse.core.runtime/.settings/org.eclipse.ant.core.prefs";
		public static const ANT_SETTINGS_PATH : String = "ANT/settings";
		public static const ANT_DEPENDENCIES_PATH : String = "ANT/dependencies";

		public static const GUPPY_PREFS_PATH : String = ".plugins/org.eclipse.core.runtime/.settings/uk.co.lmfm.guppy.prefs";
		public static const ECLIPSE_PREFS_PATH : String = ".plugins/org.eclipse.core.runtime/.settings/org.eclipse.core.resources.prefs";

		//	----------------------------------------------------------------
		//	PUBLIC MEMBERS
		//	----------------------------------------------------------------

		public var svnRevision : int = 0;

		//	----------------------------------------------------------------
		//	PRIVATE MEMBERS
		//	----------------------------------------------------------------

		private var _depth : int;
		private var _maxDepth : int;		private var _searches : int;
		private var _metadata : File;
		private var _workspaces : Array;
		//private var _libraryPath : String;
		private var _settings : SettingsVO;
		private var _config : ConfigVO;

		//	----------------------------------------------------------------
		//	CONSTRUCTOR
		//	----------------------------------------------------------------

		public function WorkspaceProxy(data : Object = null)
		{
			super(NAME, data);
		}

		//	----------------------------------------------------------------
		//	PUBLIC METHODS
		//	----------------------------------------------------------------

		public function searchForWorkspaces(directory : String, maxDepth : int = 3) : void
		{
			var workspace : File = new File();
			
			try
			{
				workspace.nativePath = directory;
			}
			catch(error : ArgumentError)
			{
				Logger.error("Workspace error: " + error.message);
			}
			
			_workspaces = [];
			_maxDepth = maxDepth + workspace.nativePath.split("/").length;
			_depth = _searches = 0;
			
			traverseDirectories(workspace);
		}

		public function isWorkspaceDirectory(file : File) : Boolean
		{
			return file.isDirectory ? file.resolvePath(METADATA_PATH).exists : false;
		}

		public function createWorkspace(location : File, settings : SettingsVO, config : ConfigVO) : void
		{
			_config = config;
			_settings = settings;
			_metadata = location.resolvePath(METADATA_PATH);
			
			var library : File = new File().resolvePath(settings.libraryPath);
			var template : File = library.resolvePath(FDT_WORKSPACE_TEMPLATE_PATH);
			
			if(template.exists)
			{
				template.addEventListener(IOErrorEvent.IO_ERROR, onCreateWorkspaceError);
				template.addEventListener(Event.COMPLETE, onCreateWorkspaceComplete);
				template.copyToAsync(_metadata);
			}
			else
			{
				Logger.error("Cannot find metadata template. Please check your library for '.metadata'. Workspace not created.");
			}
		}

		public function cloneWorkspace(request : CloneFileVO) : void
		{
			// TODO Use a queue, FileStream and monitor progress

			_metadata = request.destination.resolvePath(METADATA_PATH);

			var source : File = request.source.resolvePath(METADATA_PATH);
			
			source.addEventListener(IOErrorEvent.IO_ERROR, onMetadataCloneError);
			source.addEventListener(Event.COMPLETE, onMetadataCloneComplete);
			source.copyToAsync(_metadata);
		}

		public function configureWorkspace(workspace : File, settings : SettingsVO, config : ConfigVO) : void
		{
			_settings = settings;
			
			configureANT(workspace, settings);			configureSDK(workspace, settings, config);			configureFDT(workspace, settings);
			
			// Write last config data

			var guppyPrefs : File = workspace.resolvePath(METADATA_PATH).resolvePath(GUPPY_PREFS_PATH);
			var data : XML = new XML("<guppy></guppy>");
			
			data.modified = new Date().toString();
			data.revision = svnRevision;
			
			FileUtil.writeXML(data, guppyPrefs);
		}

		public function configureANT(workspace : File, settings : SettingsVO) : void
		{
			_settings = settings;
			
			var library : File = new File().resolvePath(_settings.libraryPath);
			var metadata : File = workspace.resolvePath(METADATA_PATH);
			var antDependanciesDir : File = library.resolvePath(ANT_DEPENDENCIES_PATH); 
			var antSettingsDir : File = library.resolvePath(ANT_SETTINGS_PATH);
			
			var antFile : File = metadata.resolvePath(ANT_PREFS_PATH);
			
			var antPref : String = "#" + new Date().toString() + "\n";
			
			antPref += "additional_entries=" + FileUtil.getContents(antDependanciesDir, false, false) + ",\n";
			antPref += "eclipse.preferences.version=1\n";
			antPref += "propertyfiles=" + FileUtil.getContents(antSettingsDir, false, false) + ",\n";
			
			FileUtil.writeUTF(antPref, antFile);
			
			Logger.info("ANT preferences set for workspace: " + workspace.name + "\nLocation:" + antFile.nativePath + "\n\nContent:\n\n" + antPref);
		}

		public function configureSDK(workspace : File, settings : SettingsVO, config : ConfigVO) : void
		{
			_settings = settings;
			
			var library : File = new File().resolvePath(_settings.libraryPath);
			var metadata : File = workspace.resolvePath(METADATA_PATH);
			var eclipsePrefs : File = metadata.resolvePath(ECLIPSE_PREFS_PATH);			var fdtPrefs : File = metadata.resolvePath(FDT_CORE_PREFS_PATH);
			
			// Set SDK path variables

			var eclipseData : String = FileUtil.readUTF(eclipsePrefs);
			var pathVariable : String;
			
			for each (var sdk : FlexSDKVO in config.sdks) 
			{
				pathVariable = "pathvariable." + sdk.path + "=" + StringUtil.stripSlashes(_settings.libraryPath + "/SDK/" + sdk.id);
				
				if(eclipseData.indexOf(pathVariable) == -1)
				{
					eclipseData += "\n" + pathVariable;
				}
			}
			
			FileUtil.writeUTF(eclipseData, eclipsePrefs);
			Logger.debug("org.eclipse.core.resources.prefs updated with new path variables:\n" + eclipseData);
			
			// Add SDK to FDT prefs

			var template : File = library.resolvePath(FDT_CORE_PREFS_TEMPLATE_PATH);
			var coreLibTemplate : String = "${name}\\=${path}\\n${name}--Type\\=${type}\\n";
			
			if(template.exists)
			{
				var fdtData : String = FileUtil.readUTF(template);
				
				fdtData = fdtData.replace(/\${name}/ig, settings.username);
				fdtData = fdtData.replace(/\${flash}/ig, settings.flashIDE);
				fdtData = fdtData.replace(/\${player}/ig, settings.flashPlayer);
				
				var coreLibs : String = '';
				
				for each (var coreLib : CoreLibraryVO in config.coreLibraries) 
				{
					if(coreLib.custom)
					{
						coreLibs += coreLibTemplate
									.replace(/\${name}/ig, coreLib.name)
									.replace(/\${path}/ig, coreLib.path)
									.replace(/\${type}/ig, coreLib.type);
					}
				}
				
				fdtData = fdtData.replace(/\${sdks}/ig, coreLibs);
			
				FileUtil.writeUTF(fdtData, fdtPrefs);
				Logger.debug("com.powerflasher.fdt.core.prefs updated with new SDKs:\n" + fdtData);
			}
			else
			{
				Logger.error("Cannot find FDT core preferences template. Please check your library for 'com.powerflasher.fdt.core.prefs'. SDKs will not be added.");	
			}
			
			/*
			
			_libraryPath = libraryPath;
			
			var library : File = new File().resolvePath(libraryPath);
			var metadata : File = workspace.resolvePath(METADATA_PATH);
			var configFile : File = library.resolvePath(CONFIG_PATH);
			var eclipsePrefs : File = metadata.resolvePath(ECLIPSE_PREFS_PATH);
			var fdtPrefs : File = metadata.resolvePath(FDT_CORE_PREFS_PATH);
			
			var config : XML = FileUtil.readXML(configFile);
			
			// Set SDK path variables

			var i : int, n : int;
			var sdks : XMLList = config..sdk;
			var sdkBase : String = "${name}\\=${path}\\n${name}--Type\\=${type}\\n";
			var custom : String = "";
			var added : Object = {};
			
			n = sdks.length();
			
			for (i = 0;i < n;i++) 
			{
				var name : String = sdks[i].@name;
				var path : String = sdks[i].@path;
				var type : String = sdks[i].@type;
				var uri : String = sdks[i].@uri;
				
				custom += sdkBase	.replace(/\${name}/ig, name)
									.replace(/\${path}/ig, path)
									.replace(/\${type}/ig, type);
				
				if(!added[path]) added[path] = "pathvariable." + path + "=" + libraryPath + "/" + uri;
			}
			
			var vars : String = FileUtil.readUTF(eclipsePrefs);
			
			for each (var pathVariable : String in added) 
			{
				// TODO If path is already defined but out of date, edit it (RegExp?)
				if(vars.indexOf(pathVariable) < 1) vars += "\n" + pathVariable;
			}
			
			FileUtil.writeUTF(vars, eclipsePrefs);
			Logger.debug("org.eclipse.core.resources.prefs updated with new path variables:\n" + vars);
			
			// Add SDK to FDT prefs

			var template : File = library.resolvePath(FDT_CORE_PREFS_TEMPLATE_PATH);
			
			if(template.exists)
			{
				vars = FileUtil.readUTF(template);
			
				// TODO Add user name and app paths to settings
				vars = vars.replace(/\${name}/ig, "User Name");
				vars = vars.replace(/\${flash}/ig, "Flash IDE Path");
				vars = vars.replace(/\${player}/ig, "Flash Player Path");
				vars = vars.replace(/\${sdks}/ig, custom);
			
				FileUtil.writeUTF(vars, fdtPrefs);
				Logger.debug("com.powerflasher.fdt.core.prefs updated with new SDKs:\n" + vars);
			}
			else
			{
				Logger.error("Cannot find FDT core preferences template. Please check your library for 'com.powerflasher.fdt.core.prefs'. SDKs will not be added.");	
			}*/
		}

		private function configureFDT(workspace : File, settings : SettingsVO) : void
		{
			_settings = settings;
			
			var library : File = new File().resolvePath(_settings.libraryPath);
			var metadata : File = workspace.resolvePath(METADATA_PATH);
			var fdtPrefs : File = metadata.resolvePath(FDT_UI_PREFS_PATH);
			
			if(fdtPrefs.exists /* TODO work with existing settings */ && false)
			{
			}
			else
			{
				var template : File = library.resolvePath(FDT_UI_PREFS_TEMPLATE_PATH);
				trace(template.exists, template.nativePath);
				if(template.exists)
				{
					var vars : String = FileUtil.readUTF(template);
					
					var snippets : String = FileUtil.readUTF(library.resolvePath(FDT_SNIPPETS_TEMPLATE_PATH)).replace(/\n/g, '\\n').replace(/\t/g, '\\t');					var headers : String = FileUtil.readUTF(library.resolvePath(FDT_HEADERS_TEMPLATE_PATH)).replace(/\n/g, '\\n').replace(/\t/g, '\\t');
					
					vars = vars.replace(/\${snippets}/ig, snippets);
					vars = vars.replace(/\${headers}/ig, headers);
			
					FileUtil.writeUTF(vars, fdtPrefs);
					Logger.debug("com.powerflasher.fdt.ui updated with new settings and templates:\n" + vars);
				}
				else
				{
					Logger.error("Cannot find FDT ui preferences template. Please check your library for 'com.powerflasher.fdt.ui.prefs'. Preferences will not be set.");
				}
			}
		}

		//	----------------------------------------------------------------
		//	PRIVATE METHODS
		//	----------------------------------------------------------------

		private function traverseDirectories(directory : File) : void
		{
			if(directory.nativePath.split("/").length > _maxDepth) return;
			
			_searches++;
			
			directory.addEventListener(FileListEvent.DIRECTORY_LISTING, onDirectoryListing);
			directory.getDirectoryListingAsync();
		}

		private function cleanMetadataFolder(metadata : File) : void
		{
			var history : File = metadata.resolvePath(".plugins/org.eclipse.core.resources/.history");			var projects : File = metadata.resolvePath(".plugins/org.eclipse.core.resources/.projects");			var index : File = metadata.resolvePath(".plugins/org.eclipse.core.resources/.root");
			
			try
			{
				FileUtil.deleteContents(history);
				FileUtil.deleteContents(projects);
				FileUtil.deleteContents(index);
			}
			catch(error : Error)
			{
				Logger.error("Problem cleaning metadata folder: " + error.message);
			}
			
			var workbench : File = metadata.resolvePath(".plugins/org.eclipse.ui.workbench/workbench.xml");
			var xml : XML = FileUtil.readXML(workbench);

			delete xml..editors.editor;
			delete xml..navigationHistory.editors.editor;
			delete xml..navigationHistory.item;
			delete xml..expanded.element;
			delete xml..mruList.file;
			
			FileUtil.writeXML(xml, workbench);
		}

		//	----------------------------------------------------------------
		//	EVENT HANDLERS
		//	----------------------------------------------------------------

		private function onDirectoryListing(event : FileListEvent) : void
		{
			var directory : File = event.currentTarget as File;
			var directories : Array = event.files;
			directory.removeEventListener(FileListEvent.DIRECTORY_LISTING, onDirectoryListing);
			
			for each (var file : File in directories) 
			{
				if(file.isDirectory)
				{
					if(file.nativePath.indexOf(METADATA_PATH) > 0)
					{
						var workspace : WorkspaceVO = new WorkspaceVO();
						
						workspace.location = file.parent;
						workspace.name = file.parent.name;
						workspace.isConfigured = false;

						var config : File = workspace.location.resolvePath(METADATA_PATH).resolvePath(GUPPY_PREFS_PATH);
						
						if(config.exists)
						{
							var data : XML = FileUtil.readXML(config);
							if(parseInt(data.revision) == svnRevision) workspace.isConfigured = true;
						}

						_workspaces.push(workspace);
						sendNotification(ApplicationConstants.FOUND_WORKSPACE, workspace);
					}
					else
					{
						traverseDirectories(file);
					}
				}
			}
			
			_searches--;
			
			if(_searches < 1)
			{
				sendNotification(ApplicationConstants.GOT_WORKSPACES, _workspaces);
			}
		}

		private function onMetadataCloneError(event : IOErrorEvent) : void
		{
			Logger.error("Error cloning workspace. Does it already exist?");
		}

		private function onCreateWorkspaceError(event : IOErrorEvent) : void
		{
			Logger.error("Error creating workspace. Does it already exist? " + event.text);
		}

		private function onMetadataCloneComplete(event : Event) : void
		{
			cleanMetadataFolder(_metadata);
			_metadata = null;
			
			Logger.info("Workspace clone complete, please restart Eclipse and select new workspace");
			
			sendNotification(ApplicationConstants.GET_WORKSPACES);
		}

		private function onCreateWorkspaceComplete(event : Event) : void
		{
			var workspace : File = _metadata.parent;
			
			Logger.info("Workspace created: " + workspace.nativePath);
			
			configureWorkspace(workspace, _settings, _config);
			sendNotification(ApplicationConstants.GET_WORKSPACES);
			_metadata = null;
		}
	}
}
